package com.three.mahjong.base.rule;

import com.three.mahjong.base.constants.MJBaseDef;
import com.three.mahjong.base.interfaces.MJRule;
import com.three.mahjong.base.model.*;
import com.three.mahjong.base.service.JudgeMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Created by pzt on 2017/6/13.
 */
@Component
public class MJBaseRule implements MJRule{

    @Autowired
    private JudgeMethod judgeMethod;

    @Override
    public boolean Peng(List<Card> cards, Card card) {
        return false;
    }

    @Override
    public boolean Gang(List<Card> cards, Card card) {
        return false;
    }

    @Override
    public boolean Hu(List<Card> cards, Card card) {
        return false;
    }

    @Override
    public CardBigType getCardBigType(int id) {
        return null;
    }

    @Override
    public Integer getSmallType(int id) {
        return null;
    }

    /**
     * 把integer的牌组转换成card类型的牌组
     * @param pais
     * @return
     */
    public List<Card> changeToCardList(List<Integer> pais) {
        List<Card> cards = new ArrayList<>();
        for(Integer pai : pais) {
            cards.add(Card.build(pai));
        }
        return cards;
    }

    /**
     * 初始化万牌列表
     * @return
     */
    public List<Integer> initTableCardOfWan(){
        List<Integer> wanPaiList = new ArrayList<>();

        int paiType = MJBaseDef.WAN_PAI_TYPE * 100;// 万类型
        for (int j = 1; j <= 9; j++) {
            int paiIndex = j * 10;// 牌下标
            for (int k = 1; k <= 4; k++) {
                int paiId = paiType + paiIndex + k;// 牌id
                wanPaiList.add(paiId);
            }
        }

        return wanPaiList;
    }

    /**
     * 初始化筒牌列表
     * @return
     */
    public List<Integer> initTableCardOfTong(){

        List<Integer> tongPaiList = new ArrayList<>();

        int paiType = MJBaseDef.TONG_PAI_TYPE * 100;// 万类型
        for (int j = 1; j <= 9; j++) {
            int paiIndex = j * 10;// 牌下标
            for (int k = 1; k <= 4; k++) {
                int paiId = paiType + paiIndex + k;// 牌id
                tongPaiList.add(paiId);
            }
        }

        return tongPaiList;
    }

    /**
     * 初始化条牌列表
     * @return
     */
    public List<Integer> initTableCardOfTiao(){
        List<Integer> tiaoPaiList = new ArrayList<>();

        int paiType = MJBaseDef.TIAO_PAI_TYPE * 100;// 万类型
        for (int j = 1; j <= 9; j++) {
            int paiIndex = j * 10;// 牌下标
            for (int k = 1; k <= 4; k++) {
                int paiId = paiType + paiIndex + k;// 牌id
                tiaoPaiList.add(paiId);
            }
        }

        return tiaoPaiList;
    }

    /**
     * 初始化风牌列表
     * @return
     */
    public List<Integer> initTableCardOfFeng(){
        List<Integer> fengPaiList = new ArrayList<>();

        int paiType = MJBaseDef.FENG_PAI_TYPE * 100;// 风类型
        for (int j = 1; j <= 7; j++) {
            int paiIndex = j * 10;// 牌下标
            for (int k = 1; k <= 4; k++) {
                int paiId = paiType + paiIndex + k;// 牌id
                fengPaiList.add(paiId);
            }
        }

        return fengPaiList;
    }

    /**
     * 初始化花牌列表
     * @return
     */
    public List<Integer> initTableCardOfHua(){
        List<Integer> huaPaiList = new ArrayList<>();

        int paiType = MJBaseDef.FLOWER_PAI_TYPE * 100;// 花类型
        for (int j = 1; j <= 8; j++) {
            int paiIndex = j * 10;// 牌下标
            int paiId = paiType + paiIndex + 1;// 牌id
            huaPaiList.add(paiId);
        }

        return huaPaiList;
    }

    /**
     * 初始化麻将牌桌
     * @param gameType 游戏类型
     * @return
     */
    public List<Integer> initTableCard(int gameType) {

        List<Integer> allPaiList = new ArrayList<>();

//        MahjongFaPaiConfig majiangFaPaiConfig = configManager.getOrThrow(MahjongFaPaiConfig.class, gameType);
//
//        int faPaiType = majiangFaPaiConfig.getFaPaiType();

        switch (3) {
            case 1://标准万筒条类型
                allPaiList.addAll(initTableCardOfWan());
                allPaiList.addAll(initTableCardOfTiao());
                allPaiList.addAll(initTableCardOfTong());
                break;
            case 2://字牌类型，包括万筒条风牌
                allPaiList.addAll(initTableCardOfWan());
                allPaiList.addAll(initTableCardOfTiao());
                allPaiList.addAll(initTableCardOfTong());
                allPaiList.addAll(initTableCardOfFeng());
                break;
            case 3://全牌，万筒条风花牌
                allPaiList.addAll(initTableCardOfWan());
                allPaiList.addAll(initTableCardOfTiao());
                allPaiList.addAll(initTableCardOfTong());
                allPaiList.addAll(initTableCardOfFeng());
                allPaiList.addAll(initTableCardOfHua());
                break;
            //后续需要增加的再补全代码，例如2人麻将，3人麻将那些
            default:
                break;
        }

        return allPaiList;
    }

    /**
     * 转化牌集合为二维数组
     * @param paiList
     * @return
     */
//    public int[][] convertTwoArrays(List<Integer> paiList) {
//
//        int[][] allPai = new int[5][10];
//        for (int paiId : paiList) {
//            int paiType = getPaiType(paiId);// 牌类型(1-万 2-筒 3-条 4-风牌 5-花牌)
//            int paiOrder = paiId / 10;// 牌下标
//            allPai[paiType - 1][0] = allPai[paiType - 1][0] + 1;
//            allPai[paiType - 1][paiOrder % 10] = allPai[paiType - 1][paiOrder % 10] + 1;
//        }
//        return allPai;
//    }

    public int[][] convertTwoArrays(List<Card> paiList) {

        int[][] allPai = new int[5][10];
        for (Card card : paiList) {
            int paiType = card.getCardBigType().ordinal();// 牌类型(1-万 2-筒 3-条 4-风牌 5-花牌)
            int paiOrder = card.getCardSmallType().getId();// 牌下标
            allPai[paiType - 1][0] = allPai[paiType - 1][0] + 1;
            allPai[paiType - 1][paiOrder % 10] = allPai[paiType - 1][paiOrder % 10] + 1;
        }
        return allPai;
    }

    /**
     * 获取牌的类型（1-万 2-筒 3-条 4-风 5-花）
     * @param pai
     * @return
     */
    public int getPaiType(int pai) {
        int paiType = pai / 100;
        return paiType;
    }

    /**
     * 获取听牌列表
     * 注：此处获取的只是牌的百位数跟十位数，例如 我听1万，1万的id有4个，111，112,113,114，此处只取11
     *
     * @param allPai
     *            玩家所有手牌
     * @param show
     *            玩家碰杠牌
     * @return
     */
    public List<Integer> getTingPai(int[][] allPai, List<ShowPai> showPais) {

        int pai = 0;
        List<Integer> tingPaiList = new ArrayList<>();
        int[][] readyAllPai = allPai;

        for (int i = 1; i <= 4; i++) {
            for (int j = 1; j <= 9; j++) {

                pai = 10 * i + j;
                boolean bool = false;
                if (readyAllPai[i - 1][j] < 4) {// 已经是一杠牌了不处理
                    readyAllPai[i - 1][0] = readyAllPai[i - 1][0] + 1;
                    readyAllPai[i - 1][j] = readyAllPai[i - 1][j] + 1;
                    bool = true;
                }// 遍历添加一张 看是否符合胡牌条件
                if (judgeMethod.fitHu(readyAllPai, showPais)) {
                    tingPaiList.add(pai);
                }

                if (bool) {
                    readyAllPai[i - 1][0] = readyAllPai[i - 1][0] - 1;
                    readyAllPai[i - 1][j] = readyAllPai[i - 1][j] - 1;
                }
            }
        }
        return tingPaiList;
    }

    /**
     * 获取玩家听的牌的剩余张数
     *
     * @param mahjongTable
     *
     * @param pai
     *
     * @param mahjongPlayer
     *
     * @return
     */
    public int getCardRemainNum(MJTable mahjongTable, int pai, MJPlayer mahjongPlayer) {
        List<Card> tableOutPaiList = new ArrayList<>();
        // 加入牌桌剩下的牌
        tableOutPaiList.addAll(changeToCardList(mahjongTable.getTablePais()));
        //加入玩家的弃牌堆，碰杠牌集合
        for (MJPlayer mahjongPlayer1 : mahjongTable.getPlayers().values()) {
            if(mahjongPlayer1.getPlayerId() == mahjongPlayer.getPlayerId()){
                //如果是玩家自己，需要把玩家手牌添加进去
                tableOutPaiList.addAll(changeToCardList(mahjongPlayer.getHandPaiList()));
            }
            tableOutPaiList.addAll(changeToCardList(mahjongPlayer.getOutPaiList()));
            List<ShowPai> showPais = mahjongPlayer.getShowPais();
            for (ShowPai showPai : showPais) {
                tableOutPaiList.addAll(changeToCardList(showPai.getPaiVector()));
            }
        }

        int paiNum = 0;
        //转换成二维数组计算剩余牌数
        int[][] tablePaiArray = convertTwoArrays(tableOutPaiList);
        int paiType = getPaiType(pai);
        int paiIndex = (pai % 100) / 10;
        paiNum = 4 - tablePaiArray[paiType - 1][paiIndex];
        return paiNum;
    }

    /**
     * 获取玩家吃牌列表
     *
     * @param targetPai 目标牌
     *
     * @param handPaiList 玩家手牌
     *
     * @return
     */
    public Map<Integer,List<Card>> calculateChiPaiInfo(int targetPai, List<Card> handPaiList) {

        Map<Integer,List<Card>> chiPaiMap = new HashMap<>();

        int[][] allPai = convertTwoArrays(handPaiList);// 将手牌转化为二维数组

        int paiType = getPaiType(targetPai); // 获取牌的类型

        if (paiType > 3) {// 只有万筒条才能吃
            return chiPaiMap;
        }

        int paiIndex = (targetPai % 100) / 10;// 获取下标位置

        int paiNextIndex = paiIndex + 1;// 获取该牌的下标后一位

        int paiNextTwoIndex = paiIndex + 2;// 获取该牌的下标后两位

        int paiLaterIndex = paiIndex - 1;// 获取该牌的下标前一位

        int paiLaterTwoIndex = paiIndex - 2;// 获取该牌的下标前两位

        switch (paiIndex) {
            case 1:// 下标是1，只能用2,3来吃
                if (allPai[paiType - 1][paiNextIndex] > 0 && allPai[paiType - 1][paiNextTwoIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiNextIndex, paiNextTwoIndex, handPaiList));
                }
                break;
            case 2:// 下标为2，只能用1,3或者3,4来吃
                if (allPai[paiType - 1][paiLaterIndex] > 0 && allPai[paiType - 1][paiNextIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiLaterIndex, paiNextIndex, handPaiList));
                }
                if (allPai[paiType - 1][paiNextIndex] > 0 && allPai[paiType - 1][paiNextTwoIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiNextIndex, paiNextTwoIndex, handPaiList));
                }
                break;
            case 8:// 下标为8，只能用7,9或者6,7来吃
                if (allPai[paiType - 1][paiLaterIndex] > 0 && allPai[paiType - 1][paiNextIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiLaterIndex, paiNextIndex, handPaiList));
                }
                if (allPai[paiType - 1][paiLaterIndex] > 0 && allPai[paiType - 1][paiLaterTwoIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiLaterIndex, paiLaterTwoIndex, handPaiList));
                }
                break;
            case 9:// 下标为9，只能用7,8来吃
                if (allPai[paiType - 1][paiLaterIndex] > 0 && allPai[paiType - 1][paiLaterTwoIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiLaterIndex, paiLaterTwoIndex, handPaiList));
                }
                break;

            default:// 其他的这些都可以
                if (allPai[paiType - 1][paiNextIndex] > 0 && allPai[paiType - 1][paiNextTwoIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiNextIndex, paiNextTwoIndex, handPaiList));
                }
                if (allPai[paiType - 1][paiLaterIndex] > 0 && allPai[paiType - 1][paiNextIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiLaterIndex, paiNextIndex, handPaiList));
                }
                if (allPai[paiType - 1][paiLaterIndex] > 0 && allPai[paiType - 1][paiLaterTwoIndex] > 0) {
                    chiPaiMap.put(targetPai,getChiPaiList(targetPai, paiLaterIndex, paiLaterTwoIndex, handPaiList));
                }
                break;
        }
        return chiPaiMap;
    }

    /**
     * 获取吃牌列表
     *
     * @param targetPai
     *            目标牌
     * @param paiIdIndex1
     *            下标1
     * @param paiIdIndex2
     *            下标2
     * @param shouPaiList
     *            玩家手牌
     * @return
     */
    public List<Card> getChiPaiList(int targetPai, int paiIdIndex1, int paiIdIndex2, List<Card> shouPaiList) {
        List<Card> chiPaiList = new ArrayList<>();
        // 获取目标牌类型
        int targetPaiType = getPaiType(targetPai);

        List<Integer> paiIndexList = new ArrayList<>();
        paiIndexList.add(paiIdIndex1);
        paiIndexList.add(paiIdIndex2);

        for (int index : paiIndexList) {
            for (Card card : shouPaiList) {
                // 获取玩家手牌类型
                int paiType = card.getCardBigType().ordinal();

                if (paiType != targetPaiType) {
                    continue;
                }

                //获取牌的下标
                int paiIndex = card.getCardSmallType().getId();

                if (paiIndex == index) {
                    chiPaiList.add(card);
                    break;
                }
            }
        }

        return chiPaiList;
    }

    /**
     * 处理玩家吃牌操作
     *
     * @param targetPai
     *            要吃的牌
     * @param chiPaiList
     *            玩家选择需要吃的关联牌
     * @param shouPaiLiist
     *            玩家手牌
     * @param paiMap
     *            玩家的牌桌摆牌
     * @return
     */
    public void dealChi(List<Integer> chiPaiList, MJPlayer mjPlayer, MJTable mahjongTable) {
        System.out.println("座位号"+mjPlayer.getSeat()+"在吃牌");
        //此处需要从牌桌上面去拿到打出这张牌的玩家的座位
        int seat = mahjongTable.getLastSeat();

        int targetPai = mahjongTable.getLastOutPai();

        //获取玩家手牌列表
        List<Integer> handPaiList = mjPlayer.getHandPaiList();

        //TODO 发送操作结果

    }

    /**
     * 玩家碰跟明杠,吃等操作之后，需要删除上一个玩家打出到弃牌堆的牌
     * @param mahjongTable
     */
    public void removeLastPlayerOutPai(MJTable mahjongTable){

        int lastOutpai = mahjongTable.getLastOutPai();

        int lastOutSeat = mahjongTable.getLastSeat();

        MJPlayer mahjongPlayer = mahjongTable.getPlayerBySeat(lastOutSeat);

        if(mahjongPlayer == null){
            return;
        }

        List<Integer> outpaiList = mahjongPlayer.getOutPaiList();

        if(outpaiList.contains(lastOutpai)){
            outpaiList.remove(Integer.valueOf(lastOutpai));
        }
    }

    /**
     * 处理碰杠牌操作
     *
     * @param targetPai
     *            目标牌
     * @param shouPaiList
     *            玩家手牌
     * @param paiMap
     *            碰杠牌集合
     */
    public void dealPeng(MJPlayer mjPlayer, MJTable mahjongTable) {
        System.out.println("座位号"+mjPlayer.getSeat()+"在碰牌");
        // 获取玩家手牌
        List<Integer> handPaiList = mjPlayer.getHandPaiList();

        int targetPai = mahjongTable.getLastOutPai();

        //从上一个玩家弃牌堆移除掉之前打出的牌
        removeLastPlayerOutPai(mahjongTable);

        int paiIndexType = targetPai / 10;// 获取牌下标类型
        List<Integer> pengPaiList = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            Iterator<Integer> iterator = handPaiList.iterator();
            while (iterator.hasNext()) {// 用迭代器移除牌
                int pai = (Integer) iterator.next();
                int paiIndex = pai / 10;
                if (paiIndex == paiIndexType) {
                    iterator.remove();
                    pengPaiList.add(pai);
                    break;
                }
            }
        }

        //此处需要获取目标牌的来源玩家座位号
        int seat = mahjongTable.getLastSeat();

        mahjongTable.setLastSeat(mjPlayer.getSeat());

        //TODO 发送操作结果给前端
    }

    /**
     * 处理碰杠牌操作
     *
     * @param targetPai
     *            目标牌
     * @param shouPaiList
     *            玩家手牌
     * @param paiMap
     *            碰杠牌集合
     */
    public void dealGang(int targetPai, MJPlayer mjPlayer, MJTable mahjongTable) {
        System.out.println("座位号" + mjPlayer.getSeat() + "在杠牌");
        // 获取玩家手牌
        List<Integer> handPaiList = mjPlayer.getHandPaiList();

        int lastPai = mahjongTable.getLastOutPai();
        int paiiNum = 4;
        int paiNum = 0;
        if (lastPai != targetPai) {// 暗杠
            paiNum = 4;
        } else {// 明杠
            paiNum = 3;
        }

        // 从上一个玩家弃牌堆移除掉之前打出的牌
        removeLastPlayerOutPai(mahjongTable);

        int paiIndexType = targetPai / 10;// 获取牌下标类型

        List<Integer> gangPaiList = new ArrayList<>();

        for (int i = 0; i < paiNum; i++) {
            Iterator<Integer> iterator = handPaiList.iterator();
            while (iterator.hasNext()) {// 用迭代器移除牌
                int pai = (Integer) iterator.next();
                int paiIndex = pai / 10;
                if (paiIndex == paiIndexType) {
                    // TODO 明杠与暗杠要区分扣除目标牌的来源（明杠是扣除桌面牌，暗杠扣除手牌）
                    iterator.remove();
                    gangPaiList.add(pai);
                    break;
                }
            }
        }

        //TODO 发送操作结果给前端
    }

    /**
     * 获取下一个玩家的座位
     * @param seat
     * @return
     */
    public int getNextSeat(int seat){
        int nextSeat = seat%4 + 1;
        return nextSeat;
    }


}
